package webrtc

import (
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"strconv"
	"sync"
	"time"
)

// 构建消息转换枚举类
const (
	// Text 发送文本消息，比如弹幕
	Text = 1
	// Candidate 发送ICE候选信息
	Candidate = 2
	// Offer 发送SDP Offer
	Offer = 3
	// Answer 发送SDP Answer
	Answer = 4
	// File 发送文件消息
	File = 5
	// Voice 发送语音消息
	Voice = 6
	// Video 发送视频消息
	Video = 7
	// Location 发送位置消息
	Location = 8
	// Heartbeat 心跳包
	Heartbeat = 9
	// AddRoom 加入房间
	AddRoom = 10
)

// WebSocketMessage 定义websocket消息格式
type WebSocketMessage struct {
	Type int         `json:"type"`
	Data interface{} `json:"data"`
	// 房间id
	Id int `json:"id"`
}

// 已经通过校验的 指定key的数据类型是int，value的数据类型是 websocket 的数。

var (
	maxConnections = 100
	mu             sync.Mutex
	// 客户端列表，key是userId，value是客户端的信令通道
	clients = make(map[int]*SignalingChannel)
	// 房间列表，key是房间id，value是房间内用户id列表
	roomUser    = make(map[int][]int)
	connections = make(chan *SignalingChannel, maxConnections)
)

func printParams() {
	log.Println("当前客户端连接数:", len(clients))
	log.Println("当前房间列表:")
	for roomId, userIds := range roomUser {
		log.Printf("房间ID: %d, 用户列表: %v\n", roomId, userIds)
	}
	// 打印所有客户端的连接信息
	log.Println("客户端连接信息:")
	for _, client := range clients {
		log.Println("用户ID:", client.UserId)
		log.Println("所属房间号:", client.RoomId)
		log.Println()
	}
}

// 定义信令服务器
type SignalingChannel struct {
	UserId int
	RoomId int
	conn   *websocket.Conn
}

func (sc *SignalingChannel) handleMessage(message []byte) {
	var signal WebSocketMessage
	if err := json.Unmarshal(message, &signal); err != nil {
		log.Printf("Error unmarshalling message: %v", err)
		return
	}
	err := json.Unmarshal(message, &signal)
	if err != nil {
		// 打印错误信息
		log.Println("解析json失败:\n", err)
	}
	log.Println("接受前端发送过来的websocket数据:\n", signal)
	// 根据不同的消息类型，进行不同的处理
	switch expression := signal.Type; expression {
	// 这个是普通文本消息，不做任何处理
	case Text:
		log.Println("接受到文本消息:\n", signal)
		break
	case AddRoom:
		log.Println("接受到加入房间消息:\n", signal)
		roomId := signal.Id
		// 将房间信息id保存到信令通道中
		sc.RoomId = roomId
		log.Println("用户:", sc.UserId, " 加入房间:", roomId)
		// 添加用户到房间
		addRoomUserMap(roomId, sc.UserId)

	// 这个是视频消息，不做任何处理
	case Video:
		log.Println("接受到视频消息:\n", signal)
		break

	// 这个是前端发送过来的ICE候选信息
	case Candidate:
		log.Println("接受到ICE候选信息:\n", signal)
		// 广播给房间内所有用户
		broadcastToRoomRemoveSelf(Candidate, signal.Data, sc.RoomId, sc.UserId)
		break
	// 这个是SDP Answer，不做任何处理
	case Answer:
		log.Println("接受到SDP Answer:\n", signal)
		broadcastToRoomRemoveSelf(Answer, signal.Data, sc.RoomId, sc.UserId)
		break

	// 这个是SDP Offer，不做任何处理
	case Offer:
		log.Println("接受到SDP Offer:\n", signal)
		broadcastToRoomRemoveSelf(Offer, signal.Data, sc.RoomId, sc.UserId)
		break

	// 这个是文件消息，不做任何处理
	case File:
		log.Println("接受到文件消息:\n", signal)
		break

	// 这个是位置消息，不做任何处理
	case Location:
		log.Println("接受到位置消息:\n", signal)
		break

	// 这个是视频消息，不做任何处理
	case Voice:
		log.Println("接受到语音消息:\n", signal)
		break
	case Heartbeat:
		log.Println("接受到心跳消息:\n", signal)
		break

	// 这个是未知消息，不做任何处理
	default:
		log.Println("未知消息类型:\n", signal)
		break
	}
}

// Send 信令服务器发送消息
func (sc *SignalingChannel) Send(messageType int, data interface{}) error {
	// 把消息转为JSON格式
	signalJSON, err := json.Marshal(&WebSocketMessage{Type: messageType, Data: data})
	if err != nil {
		return err
	}
	// 使用websocket连接发送消息给前端
	err = sc.conn.WriteMessage(websocket.TextMessage, signalJSON)
	if err != nil {
		return err
	}
	return nil
}

// Receive 信令服务器接收消息
func (sc *SignalingChannel) Receive() {
	for {
		messageType, message, err := sc.conn.ReadMessage()

		// 如果消息类型是退出类型
		if messageType == websocket.CloseMessage || messageType == -1 {
			// 首先移除客户端
			removeClient([]int{sc.UserId})
			log.Println("移除客户端成功")
			// 移除房间内的成员信息
			if sc.RoomId != 0 {
				removeRoomUserMap(sc.RoomId, sc.UserId)
			}
			// 关闭连接
			err = sc.conn.Close()
			if err != nil {
				// 打印错误信息
				log.Println("go关闭 Error closing WebSocket connection:", err)
			}
			break
		}
		// 如果是错误，就退出循环
		if err != nil {
			log.Printf("读取消息发生错误: %v", err)
			break
		}

		sc.handleMessage(message)
	}
}

// HandleWebSocket 处理WebSocket连接
func HandleWebSocket(c *gin.Context) {
	// step 1: 解析数据，参数
	log.Println("websocket连接开始")
	tokenStr := c.Query("token")

	token, _ := strconv.Atoi(tokenStr)
	log.Println("HandleWebSocket-获取到用户信息，用户id为:", token)

	// step 2: 升级http请求为websocket
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println("WebSocket upgrade failed:", err)
		return
	}
	log.Println("连接成功")

	// step 3: 创建信令通道
	signalingChannel := &SignalingChannel{conn: conn, UserId: token}
	err = signalingChannel.Send(Text, "Hello")
	if err != nil {
		// 打印错误信息
		log.Println("go发送 Error sending message:", err)
	}

	// step 4: 接收消息
	go signalingChannel.Receive()

	// step 5: 将信令通道添加到客户端列表中
	addClient(token, signalingChannel)

}

// addClient 添加客户端
func addClient(userId int, sc *SignalingChannel) {
	log.Println("addClient添加客户用户id:", userId)
	mu.Lock()
	defer mu.Unlock()
	clients[userId] = sc
}

// removeClient 移除客户端
func removeClient(userIds []int) {
	mu.Lock()
	defer mu.Unlock()
	for _, userId := range userIds {
		delete(clients, userId)
	}
}

// addRoomUserMap 添加房间用户
func addRoomUserMap(roomId int, UserId int) {
	// 假设 sc.UserId 是一个单独的用户ID，需要将其转换为切片以便 append
	var userIdSlice []int
	userIdSlice = append(userIdSlice, UserId) // 将单个用户ID放入切片
	// 现在将这个用户ID切片添加到 roomUser 中对应 roomId 的位置
	roomUser[roomId] = append(roomUser[roomId], userIdSlice...)

	log.Println("添加房间用户:", roomId, roomUser[roomId])

	// 打印客户端列表
	printParams()
}

// removeRoomUserMap 移除房间用户
func removeRoomUserMap(roomId int, userId int) {
	// 假设需要移除的 UserId 是一个整数值
	userIdToRemove := userId

	// 创建一个新的切片，其中不包含需要移除的元素
	newSlice := make([]int, 0)
	for _, userId := range roomUser[roomId] {
		if userId != userIdToRemove {
			newSlice = append(newSlice, userId)
		}
	}

	// 更新 roomUser[roomId] 为新的切片
	roomUser[roomId] = newSlice
}

// 广播消息，每个客户端都会收到
func broadcast(messageType int, data interface{}) {
	for _, client := range clients {
		err := client.Send(messageType, data)
		if err != nil {
			log.Println("go广播 Error sending message:", err)
		}
	}
}

// 给房间内所有用户广播消息
func broadcastToRoom(messageType int, data interface{}, roomId int) {
	log.Println("给房间内所有用户广播消息:", data)
	for _, userId := range roomUser[roomId] {
		sendToUser(messageType, data, userId)
	}
}

// 给房间内所有用户发送消息排除自己
func broadcastToRoomRemoveSelf(messageType int, data interface{}, roomId int, selfUserId int) {
	for _, userId := range roomUser[roomId] {
		if userId == selfUserId {
			continue
		}
		sendToUser(messageType, data, userId)
	}
}

// 给指定用户发送消息
func sendToUser(messageType int, data interface{}, userId int) {
	client := clients[userId]
	err := client.Send(messageType, data)
	if err != nil {
		log.Println("go发送给 ", userId, "失败，失败原因:", err)
	}
}

// 心跳，一般是前端发送给后端，此处不需要
func heartbeat(sc *SignalingChannel) {
	// 在上面的代码中，我们使用time.NewTicker函数创建了一个定时器，每隔5秒触发一次。
	//然后，我们使用conn.WriteMessage函数发送一个WebSocket ping消息作为心跳包。
	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for {
		log.Println("heartbeat")
		select {
		case <-ticker.C:
			err := sc.Send(Heartbeat, "心跳")
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}
